/*appmanager.c*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/msg.h>
//#include <sys/epoll.h>

#include "appmanager.h"

#define MAX_ARGS 64
#define ARG_SIZE 256

#define	APP_FIFO_NAME	"/tmp/appmgr.fifo"

typedef void (*app_cmd_func)(char *buff, int argc, char *argv[]);
typedef struct{
	char cmd_name[32];
	app_cmd_func cmd_func;
}app_mgr_cmds_t;

static volatile int m_is_upgrading = 0;
static void _app_mgr_upgrade(char *buff, int argc, char *argv[]);

static app_mgr_cmds_t app_mgr_cmds[] = {
	{
		"upgrade",
		_app_mgr_upgrade,
	},
	// {
	// 	"heartbeat",
	// 	_app_mgr_upgrade,
	// },
};

#define CMDS_COUNT	(sizeof(app_mgr_cmds)/sizeof(app_mgr_cmds[0]))

/*
 * 使用system() 的方式启动指定cmd
 * */
static void _app_mgr_cmd_exe(const char *cmd)
{
#if 1	
	system(cmd);
#else
	pid_t pid = fork();

	if (0 == pid)
	{
        // child process here
        execlp("ls", "ls", "-l", (char *)NULL);
        
        perror("execlp failed");
        //exit(EXIT_FAILURE);		
	}
	else if (pid > 0)
	{
		//father process here
	}
	else
	{
		perror("fork failed");
	}
#endif	
}

//cmd: "system ls /tmp", is to execute: "ls /tmp"
static void _app_mgr_cmd_system(const char *cmd)
{
	char *cmd_argv = strstr(cmd, " ");
	if (!cmd_argv)
		return;

	while(1)
	{
		if (*cmd_argv == ' ')
			cmd_argv ++;
		else
			break;
	}
	printf("%s(). system: %s\n", __func__, cmd_argv);
	_app_mgr_cmd_exe(cmd_argv);
}

#if 1
static int _app_mgr_prepare_upgrade(const char *app_name)
{
	char cmds[] = 
	{
		"mkdir /tmp/upg_lib/lib -p\n"
		"mkdir /tmp/upg_lib/usr/lib -p\n"

		"cp /lib/libpthread.so.0 /tmp/upg_lib/lib -vf\n"
		"cp /lib/libc.so.6 /tmp/upg_lib/lib -vf\n"
		"cp /lib/ld.so.1 /tmp/upg_lib/lib -vf\n"
		"cp /lib/libm.so.6 /tmp/upg_lib/lib -vf\n"
		"cp /lib/libstdc++.so.6 /tmp/upg_lib/lib -vf\n"
		"cp /lib/libgcc_s.so.1 /tmp/upg_lib/lib -vf\n"
		"cp /lib/libresolv.so.2 /tmp/upg_lib/lib -vf\n"
		"cp /usr/lib/libhcfota.so /tmp/upg_lib/usr/lib -vf\n"
		"cp /usr/lib/liblvgl.so /tmp/upg_lib/usr/lib -vf\n"

		"mount --bind /tmp/upg_lib/lib /lib\n"
		"mount --bind /tmp/upg_lib/usr/lib /usr/lib\n"
	};
	_app_mgr_cmd_exe(cmds);

	return 0;
}
#endif

static void _app_mgr_stop_service(void)
{
	//disable hotplug
	//stop wifi monitor shell
	//stop wpa_supplicant
	char cmds[] = 
	{
		"echo \"\" > /proc/sys/kernel/hotplug\n"
		"killall daemon_monitor.sh\n"
		"killall hostapd\n"
		"killall wpa_supplicant\n"
		//"killall hcdaemon\n"
		"ifconfig wlan0 down\n"
		"ifconfig p2p0 down\n"
		//"modprobe -r 8188fu\n"
		
		"sleep 1\n"
	};
	printf("%s():\n%s\n", __func__, cmds);
	_app_mgr_cmd_exe(cmds);
}

/*
* buff(command): upgrade -k appname -s upgradeapp -u /tmp/HCFOTA.bin -z 0,0,1280,720,1920,1080 -f 180,0,0
* 		-k: kill application with name.
* 		-s: start upgrade app name, with upgrade args.
 */
static void _app_mgr_upgrade(char *buff, int argc, char *argv[])
{
    int opt;
    volatile int prepare = -1;

    char app_kill[64] = {0};
    char app_start[64] = {0};
    char cmd_str[128] = {0};
    while ((opt = getopt(argc, &argv[0], "k:s:")) != EOF) 
    {
        switch (opt) 
        {
        case 'k':
            strncpy(app_kill, optarg, sizeof(app_kill));
            break;
        case 's':
        	strncpy(app_start, optarg, sizeof(app_start));
            break;
        default:
            break;        
        }
    }

    //step 1: kill application
    if (strlen(app_kill))
    {
    	int check_count = 0;
    	m_is_upgrading = 1;
    	int app_pid = app_mgr_pid_get(app_kill);

    	snprintf(cmd_str, sizeof(cmd_str), "kill -9 %d", app_pid);
    	printf("%s(), line:%d. cmd: %s\n", __func__, __LINE__, cmd_str);
    	do{
    		_app_mgr_cmd_exe(cmd_str);
    		if (0 == app_mgr_app_is_runing(app_kill))
    			break;
    		else
    			usleep(100*1000);
    	}while(check_count ++ > 5);
    	if (check_count >= 5)
    		printf("%s(), line:%d. can not kill %s\n", __func__, __LINE__, app_kill);
    }

    //step 2: stop some services
    _app_mgr_stop_service();


    //step 3: start flash burning
    if (strlen(app_start))
    {
    	m_is_upgrading = 1;
    	char *app_argv = strstr(buff, app_start);
    	//app_argv: upgradeapp -u /tmp/HCFOTA.bin -z 0,0,1280,720,1920,1080 -f 180,0,0
    	if (app_argv)
    	{
		   	printf("%s(), line:%d. cmd: %s\n", __func__, __LINE__, app_argv);
		   	prepare = _app_mgr_prepare_upgrade(app_start);

			if (!prepare)
			{
				//copy /usr/bin/upgradeapp to /tmp/upgradeapp
				snprintf(cmd_str, sizeof(cmd_str), "cp /usr/bin/%s /tmp", app_start);
				_app_mgr_cmd_exe(cmd_str);

				//"app_argv: /tmp/upgradeapp -u /tmp/HCFOTA.bin -z 0,0,1280,720,1920,1080 -f 180,0,0"
				snprintf(cmd_str, sizeof(cmd_str), "/tmp/%s", app_argv);
			}
			else
			{
				snprintf(cmd_str, sizeof(cmd_str), "%s", app_argv);
			}	

			printf("%s(), line:%d. %s!\n", __func__, __LINE__, cmd_str);
		   	_app_mgr_cmd_exe(cmd_str);
		   	printf("%s(), line:%d. upgrade done!\n", __func__, __LINE__);
    	}
    }

}

static void _app_mgr_cmd_process(char *buff, int argc, char *argv[])
{
	int i;

	for (i = 0; i < CMDS_COUNT; i ++)
	{
		if (!strcmp(app_mgr_cmds[i].cmd_name, argv[0]))
		{
			if (app_mgr_cmds[i].cmd_func)
			{
				app_mgr_cmds[i].cmd_func(buff, argc, argv);
				break;
			}	
		}
	}
}


static int _app_mgr_encap_command(const char *command, char *argv[]) 
{
    char *buffer = strdup(command); 
    char *arg = strtok(buffer, " "); 
    int count = 0;

    while (arg != NULL && count < MAX_ARGS) {
        argv[count++] = strdup(arg); 
        arg = strtok(NULL, " "); 
    }

    free(buffer); 
    return count; 
}


//use message queue for PIC communication
#define APP_IPC_MSG

#ifdef APP_IPC_MSG

#ifdef CONFIG_APPS_MGR_MSG_KEY
  #define APP_QUEUE_KEY	CONFIG_APPS_MGR_MSG_KEY
#else
  #define APP_QUEUE_KEY	12345678
#endif


#define MAX_MSG_SIZE    256
typedef struct{
    long type;
    char buff[MAX_MSG_SIZE];
}msg_queue_t;    

/*
 * 基于管道或者消息队列实现进程通信
 * */
static int _app_mgr_msg_open(void)
{
	int msgid = -1;
    int count = 0;
	key_t key = (key_t)APP_QUEUE_KEY;

    printf("%s(), line:%d. key:%d \n", __func__, __LINE__, key);
    do{
		msgid = msgget((key_t)key, 0666 | IPC_CREAT);    	
		if (msgid > 0)
			break;
		else if (msgid == 0)
			msgctl(msgid, IPC_RMID, NULL);
    }while(count++ < 10);

    if (msgid > 0)
    {
	    printf("%s(), line:%d. create msg id(%d): %d\n", __func__, __LINE__,count, msgid);
	    return msgid;
    }
    else
    {
    	printf("%s(), line:%d. msgget failed(%d):%d\n", __func__, __LINE__, count, msgid);
    	return -1;
    }

}

static void _app_mgr_msg_close(int msg_id)
{
	msgctl(msg_id, IPC_RMID, NULL);
}


//return the received length of data
static int _app_mgr_msg_recv(int msg_id, void *buffer, unsigned long length)
{
    msg_queue_t msg_queue;

    //block to received message. set IPC_NOWAIT if no block
    //if (msgrcv(msgid, (void *)&msg_queue, length, 0, IPC_NOWAIT) == -1){
    if (msgrcv(msg_id, (void *)&msg_queue, length, 0, 0) == -1){
        return -1;
    }    
    memcpy(buffer, msg_queue.buff, length);

    return length;
}

#else
static int _app_mgr_msg_open(void)
{
	int ret = -1;

	umask(0);
    ret = mkfifo(APP_FIFO_NAME, 0664);
    if (ret < 0 && errno != EEXIST)
    {
        perror("mkfifo error");
        return -1;
    }

    printf("open pipe %s......\n", APP_FIFO_NAME);
    int fd = open(APP_FIFO_NAME, O_RDONLY);
    if (fd < 0)
    {
        perror("open pipe error");
        return -1;
    }

    return fd;
}

static void _app_mgr_msg_close(int msg_id)
{
	int fd = msg_id;
	close(fd);
}


//return the received length of data
static int _app_mgr_msg_recv(int msg_id, void *buffer, unsigned long length)
{
	int fd = msg_id;
    return read(fd, buffer, length);
}
#endif

/*
 *  命令处理函数，轮询接收APP 发送命令，
 *  解析/执行指定的命令(主要命令: upgrade)
 * */
static void *_app_mgr_cmd_task(void *arg)
{
	(void)arg;
	int ret;
	char buf[256] = {0};
    int cmd_argc;
	char *cmd_argv[MAX_ARGS];
	int msg_id = -1;

	msg_id = _app_mgr_msg_open();
	if (msg_id < 0)
		return NULL;

    while(1)
    {
        int ret = _app_mgr_msg_recv(msg_id, buf, sizeof(buf)-1);
        if (ret > 0)
        {
        	cmd_argc = _app_mgr_encap_command(buf, cmd_argv);
        	if (0 == strcmp(cmd_argv[0], "system"))
        	{
        		_app_mgr_cmd_system(buf);
        	}
        	else 
        	{
        		_app_mgr_cmd_process(buf, cmd_argc, cmd_argv);
        	}
        }
        else
        {
        	sleep(1);
        }
    }
    _app_mgr_msg_close(msg_id);


	return NULL;
}

static void _app_mgr_cmd_start(void)
{
    pthread_t thread_id = 0;
    pthread_attr_t attr;

    //create the message task
    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, 0x2000);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); //release task resource itself
    pthread_create(&thread_id, &attr, _app_mgr_cmd_task, NULL);
    pthread_attr_destroy(&attr);
}

int app_mgr_is_upgrading(void)
{
	return 	m_is_upgrading;
}

#if 0
static void app_test_msg_create(void)
{
	int key = 1;
    int msgid = -1;
	char buffer[128];

    while (1)
    {
        printf("Enter msg key: \n");
        fgets(buffer, sizeof(buffer), stdin);
        key = atoi(buffer);
        printf("you enter key: %d\n", key);

        if (key == 88888888)
        	return;
        msgid = msgget((key_t)key, 0666 | IPC_CREAT);
		printf("Create msg id: %d\n", msgid);        

		msgctl(msgid, IPC_RMID, NULL);
		sleep(1);
    }

}
#endif

// Check the application if still alive, restart application if it is dead
//cmd: appmanager app1 app2 ...
// inittab pass argv : hcprojector 
int main(int argc, char *argv[])
{
	printf("Hello, I am app manager!\n\n");	

	int daemon_count = argc - 1;
	int i;
	for (i = 0; i < daemon_count; i ++)
	{
		app_mgr_daemon_start(argv[i+1]);
	}

	_app_mgr_cmd_start();

    while(1)
    	sleep(10);

    return 0;

}
